package com.juicygames.framework.android;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

import com.juicygames.undoer.fps.FpsCounter;
import com.juicygames.undoer.world.World;

class GameThread extends Thread {
	public GameThread(SurfaceHolder surfaceHolder) {
		this.surfaceHolder = surfaceHolder;
	}

	@Override
	public void run() {
		while (game == null) {
			try {
				wait(); 
			} catch (InterruptedException ex) {}
			continue;
		}

		long time = System.nanoTime();

		final float maxFrameRate = 30.0f;
		final float minFrameRate = 6.0f;
		final float minTimeStepSecs = 1.0f / maxFrameRate;
		final float maxTimeStepSecs = 1.0f / minFrameRate;
		
		// For calculating fps
		FpsCounter fpsCounter = new FpsCounter(time);

		while (running) {
			
			long currentTime = System.nanoTime();
			
			fpsCounter.count(currentTime);
			
			float timeStep = tryToSleep(time, minTimeStepSecs, maxTimeStepSecs,
					currentTime);

			updateGame(fpsCounter.getFps(), timeStep);
		}
	}

	private float tryToSleep(long time, final float minTimeStepSecs,
			final float maxTimeStepSecs, long currentTime) {
		
		float timeStep = (float)(currentTime - time) * 1.0e-9f;
		time = currentTime;
		if (timeStep < minTimeStepSecs) {
			float remainingTime = minTimeStepSecs - timeStep;
			timeStep = minTimeStepSecs;
			try {
				long sleepMilliseconds = (long)(remainingTime * 1.0e3f);
				remainingTime -= sleepMilliseconds * 1.0e-3f;
				int sleepNanoseconds = (int)(remainingTime * 1.0e9f);
				Thread.sleep(sleepMilliseconds, sleepNanoseconds);
			} catch (InterruptedException ex) {}
		} else {
			yield();
		}
		timeStep = Math.max(timeStep, minTimeStepSecs);
		timeStep = Math.min(timeStep, maxTimeStepSecs);
		return timeStep;
	}

	private void updateGame(float framesPerSecond, float timeStep) {
		Canvas canvas = null;
		try {
			canvas = surfaceHolder.lockCanvas(null);
			synchronized (game) {
				game.update(timeStep, framesPerSecond, canvas);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (canvas != null)
				surfaceHolder.unlockCanvasAndPost(canvas);
		}
	}

	public void setGame(World game) {
		this.game = game;
	}

	public void halt() {
		running = false;
	}

	boolean running = true;
	AndroidGame game;
	SurfaceHolder surfaceHolder;
}
